Instalar librerias necesarias (en modo silencioso -q):
[15]:
!wget -q https://raw.githubusercontent.com/arqlm/arqlm.github.io/main/_static/libraries.txt
!pip install -q -r /content/libraries.txt
!pip install -q --upgrade plotly[kaleido]
!rm -r /content/libraries.txt
!rm -r /content/sample_data/ ## Esta linea no es necesaria cuando no se trabaja en colab.google
'wget' is not recognized as an internal or external command,
operable program or batch file.
ERROR: Could not open requirements file: [Errno 2] No such file or directory: '/content/libraries.txt'
'rm' is not recognized as an internal or external command,
operable program or batch file.
'rm' is not recognized as an internal or external command,
operable program or batch file.
[16]:
## Importar librerías
import pandas as pd
import numpy as np
import base64
import datetime
import io
import plotly.io as pio
import plotly.graph_objects as go
import plotly.express as px
import plotly.offline as py
# pio.renderers.default = "colab"
pio.renderers.default = "notebook" # usar esta linea en jupyter notebook o vscode
5. Definición de unidades o dominios de estimación
La decisión de estacionariedad es clave para la inferencia estadística. La inferencia debe realizarse utilizando muestras que pertenezcan una misma población (dominio). Además, en el espacio, las propiedades estadísticas de las ubicaciones utilizadas para inferir información (ley de mineral) una ubicación no muestreada deben ser consistentes, para evitar sesgos. Si las propiedades espaciales de la variable cambian con la ubicación, la inferencia se vuelve problemática.
Para abordar este problema, se definen dominios. En el contexto del modelado en geociencias, estos dominios deben mostrar propiedades similares desde el punto de vista geológico, y muchas veces también deben mostrar consistencia respecto a la variable de respuesta. Por ejemplo, en depósitos minerales, un dominio debe agrupar ubicaciones con una distribución de leyes similar.
Así, los dominios estacionarios se definen en base a las propiedades geológicas y a las propiedades estadísticas de la variable estudiada. Llamaremos a estos dominios ‘unidades geológicas’ cuando el problema estadístico se encuentre el contexto minero de estimación de recursos.
Definición de unidades geológicas (dominios) para la estimación de recursos (no es una receta, sino primeros pasos que requieren iteraciones adicionales con aporte geológico):
Realizar un análisis estadístico de las leyes relevantes por categoría de atributo geológico (típicamente litología, tipo de alteración y zona mineral).
Identificar categorías que muestran un comportamiento similar en términos de la distribución estadística, observando los gráficos de probabilidad.
Unir categorías geológicas, considerando la similitud estadística, su ubicación espacial y la coherencia geológica. Por ejemplo, agrupar tipos de rocas similares y no unir materiales que requieran diferentes procesos de tratamiento.
Actualizar las estadísticas y revisar.
Los primeros dos pasos ya fueron realizados en las secciones anteriores (ver Búsqueda de controles geológicos). Procedamos a continuación con el punto 3.
6. Creación de Unidades Geológicas
6.1. Carga de archivos
[17]:
import pandas as pd
import numpy as np
import warnings
warnings.simplefilter(action="ignore")
# Cargar base de datos en pandas
DH = pd.read_csv('EvYacData.csv', sep=',', encoding='latin1')
DH['cu_pct'][DH['cu_pct'] <= 0] = np.nan
DH
[17]:
| Este | Norte | Elevación | au_ppm | ag_ppm | cu_pct | aucn_ppm | cucn_ppm | Zmin | Alte | Lito | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 472186.686 | 6925804.447 | 4220.763 | -99.00 | -99.0 | NaN | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM |
| 1 | 472187.202 | 6925805.493 | 4213.861 | 0.30 | 2.3 | 0.011 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM |
| 2 | 472187.343 | 6925805.770 | 4211.986 | 0.47 | 16.2 | 0.032 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM |
| 3 | 472187.493 | 6925806.060 | 4210.013 | 0.31 | 2.3 | 0.018 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM |
| 4 | 472187.642 | 6925806.351 | 4208.040 | 0.29 | 2.1 | 0.010 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 77836 | 471293.198 | 6925823.101 | 3761.674 | 0.03 | 1.0 | 0.002 | -99.0 | -99.0 | BACK | PROP | ECS |
| 77837 | 471293.540 | 6925824.040 | 3759.942 | 0.01 | 0.7 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS |
| 77838 | 471293.882 | 6925824.980 | 3758.209 | 0.01 | 0.4 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS |
| 77839 | 471294.224 | 6925825.920 | 3756.477 | 0.06 | 1.3 | 0.004 | -99.0 | -99.0 | BACK | PROP | ECS |
| 77840 | 471294.607 | 6925826.972 | 3754.538 | 0.06 | 0.5 | 0.003 | -99.0 | -99.0 | BACK | PROP | ECS |
77841 rows × 11 columns
Continuamos creando una nueva variable en nuestra base de datos en donde guardaremos la definición de Unidades Geologicas (UG) que, por defecto, tendra un valor no definido, -99:
[18]:
DH['UG'] = -99
DH
[18]:
| Este | Norte | Elevación | au_ppm | ag_ppm | cu_pct | aucn_ppm | cucn_ppm | Zmin | Alte | Lito | UG | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 472186.686 | 6925804.447 | 4220.763 | -99.00 | -99.0 | NaN | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | -99 |
| 1 | 472187.202 | 6925805.493 | 4213.861 | 0.30 | 2.3 | 0.011 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | -99 |
| 2 | 472187.343 | 6925805.770 | 4211.986 | 0.47 | 16.2 | 0.032 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | -99 |
| 3 | 472187.493 | 6925806.060 | 4210.013 | 0.31 | 2.3 | 0.018 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | -99 |
| 4 | 472187.642 | 6925806.351 | 4208.040 | 0.29 | 2.1 | 0.010 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | -99 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 77836 | 471293.198 | 6925823.101 | 3761.674 | 0.03 | 1.0 | 0.002 | -99.0 | -99.0 | BACK | PROP | ECS | -99 |
| 77837 | 471293.540 | 6925824.040 | 3759.942 | 0.01 | 0.7 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | -99 |
| 77838 | 471293.882 | 6925824.980 | 3758.209 | 0.01 | 0.4 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | -99 |
| 77839 | 471294.224 | 6925825.920 | 3756.477 | 0.06 | 1.3 | 0.004 | -99.0 | -99.0 | BACK | PROP | ECS | -99 |
| 77840 | 471294.607 | 6925826.972 | 3754.538 | 0.06 | 0.5 | 0.003 | -99.0 | -99.0 | BACK | PROP | ECS | -99 |
77841 rows × 12 columns
Como ya se mencionó anteriormente, la definición de UGs es un proceso iterativo. Partiremos definiendo tres UGs a partir de las Zonas Minerales. La creación de UGs requiere un uso intensivo de la lógica y, por lo tanto, de operadores lógicos. Escencialmente, en esta primera etapa nos interesa separar los oxidos, de la zona mixta y transcicion —las cuales uniremos en una sola unidad—, del resto de las muestras (0 —no definido—, probablemente esteril o gravas):
[19]:
## 1.- Si la muestra pertenecen a la zona de transicion o el background, definimos temporalmente una UG de mayor ley, 3
DH['UG'][(DH['Zmin'] == 'TRANS') | (DH['Zmin'] == 'BACK')] = 2
## 2.- Si la muestra pertenecen a la zona de oxidos, definimos temporalmente una UG de ley intermedia, 2
DH['UG'][(DH['Zmin'] == 'OXI')] = 1
## 3.- Finalmente, si la muestra pertenecen no pertenece ni a la UG 3 ni a la 2, entonces las muestras son las de menor ley y las agrupamos en la UG 1
## DH['UG'][(DH['Zmin'] != 'OXI') & (DH['Zmin'] != 'TRANS') & (DH['Zmin'] != 'BACK')] = 0 ## Podemos usar este comando, o equivalentemente:
DH['UG'][(DH['UG'] != 2) & (DH['UG'] != 1)] = 0
## Mostramos el resultado final
DH
[19]:
| Este | Norte | Elevación | au_ppm | ag_ppm | cu_pct | aucn_ppm | cucn_ppm | Zmin | Alte | Lito | UG | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 472186.686 | 6925804.447 | 4220.763 | -99.00 | -99.0 | NaN | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 1 | 472187.202 | 6925805.493 | 4213.861 | 0.30 | 2.3 | 0.011 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 2 | 472187.343 | 6925805.770 | 4211.986 | 0.47 | 16.2 | 0.032 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 3 | 472187.493 | 6925806.060 | 4210.013 | 0.31 | 2.3 | 0.018 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 4 | 472187.642 | 6925806.351 | 4208.040 | 0.29 | 2.1 | 0.010 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 77836 | 471293.198 | 6925823.101 | 3761.674 | 0.03 | 1.0 | 0.002 | -99.0 | -99.0 | BACK | PROP | ECS | 2 |
| 77837 | 471293.540 | 6925824.040 | 3759.942 | 0.01 | 0.7 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 2 |
| 77838 | 471293.882 | 6925824.980 | 3758.209 | 0.01 | 0.4 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 2 |
| 77839 | 471294.224 | 6925825.920 | 3756.477 | 0.06 | 1.3 | 0.004 | -99.0 | -99.0 | BACK | PROP | ECS | 2 |
| 77840 | 471294.607 | 6925826.972 | 3754.538 | 0.06 | 0.5 | 0.003 | -99.0 | -99.0 | BACK | PROP | ECS | 2 |
77841 rows × 12 columns
[20]:
import numpy as np
import plotly.graph_objects as go
import scipy.stats as stats
from statsmodels.distributions.empirical_distribution import ECDF
# Plot all probability curves in the same figure
fig = go.Figure()
for category in sorted(DH.groupby('UG').groups.keys()):
values = DH.groupby('UG').get_group(category)['cu_pct'].dropna().values
# Sort values
values_sorted = np.sort(values)
n = len(values_sorted)
cumprob = (np.arange(1, n+1) - 0.5) / n
normal_scores = stats.norm.ppf(cumprob)
fig.add_trace(go.Scatter(
x=values_sorted,
y=normal_scores,
mode='markers',
marker=dict(size=5, opacity=0.7),
name=str(category)
))
# Custom y-axis to match imagen.png
y_ticks = stats.norm.ppf([0.0001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 0.99999])
y_ticklabels = ['0.001%','1%', '5%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '95%', '99%', '99.999%']
fig.update_layout(
title="Distribuciones preliminares de Dominios",
title_x=0.5,
xaxis_title='Cu [%]',
xaxis=dict(
type='log',
),
yaxis_title='Probabilidad',
yaxis=dict(
tickmode='array',
tickvals=y_ticks,
ticktext=y_ticklabels,
range=[stats.norm.ppf(0.00001), stats.norm.ppf(0.99999)]
),
width=800,
height=600,
font_family="Times New Roman",
font_size=14,
font_color="black",
plot_bgcolor='white',
autosize=False,
showlegend=True
)
fig.update_layout(
legend_title_text='UGs',
legend=dict(
itemsizing='constant',
font=dict(size=14),
bgcolor='rgba(255,255,255,0.8)',
bordercolor='black',
borderwidth=1
)
)
fig.update_xaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_yaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_layout(hovermode=False)
fig.show()
A continuacion, haremos un zoom a las dos dsitribuciones principales, realizando lo que se denomina como un quiebre de las distribuciones: ‘romperemos’ estas distribuciones de acuerdo a una segunda variable geológica: en este caso, por la alteración:
A continuacion, haremos un zoom a las dos dsitribuciones principales, realizando lo que se denomina como un quiebre de las distribuciones: ‘romperemos’ estas distribuciones de acuerdo a una segunda variable geológica: en este caso, por la alteración:
Resumen de pasos para subdividir UGs según alteración:
Para UG 2:
Si la muestra pertenece a UG 2 y la alteración es POT_BT, POT_KFELD o BACK → asignar UG 23 (máxima ley).
Si la muestra pertenece a UG 2 y la alteración es SER_CLO o QTZ_SER_PY → asignar UG 22 (mediana ley).
El resto de UG 2 → asignar UG 21 (resto).
Para UG 1:
Si la muestra pertenece a UG 1 y la alteración es SER_CLO o QTZ_SER_PY → asignar UG 13 (máxima ley).
Si la muestra pertenece a UG 1 y la alteración es PROP o ILL_CLO → asignar UG 12 (mediana ley).
El resto de UG 1 → asignar UG 11 (resto).
[21]:
##### Para la UG 3 ##########################################################################
## 1.- Si la muestra pertenecen a la UG 2, generamos una distribucion de maxima ley, UG 23 usando las categorías de alteración POT_BT, POT_KFELD, y BACK
DH['UG'][(DH['UG'] == 2) & ((DH['Alte'] == 'POT_BT') | (DH['Alte'] == 'POT_KFELD') | (DH['Alte'] == 'BACK'))] = 23
## 2.- Si la muestra pertenecen a la UG 2, generamos una distribucion de mediana ley, UG 22 usando las categorías de alteración SER_CLO y QTZ_SER_PY
DH['UG'][(DH['UG'] == 2) & ((DH['Alte'] == 'SER_CLO') | (DH['Alte'] == 'QTZ_SER_PY'))] = 22
## 3.- Finalmente, el resto pasa a ser definido como UG 21
DH['UG'][(DH['UG'] == 2)] = 21
##### Para la UG 1 ##########################################################################
## 1.- Si la muestra pertenecen a la UG 1, generamos una distribucion de maxima ley, UG 13 usando las categorías de alteración POT_BT, POT_KFELD, y BACK
DH['UG'][(DH['UG'] == 1) & ((DH['Alte'] == 'SER_CLO') | (DH['Alte'] == 'QTZ_SER_PY'))] = 13
## 2.- Si la muestra pertenecen a la UG 1, generamos una distribucion de mediana ley, UG 12 usando las categorías de alteración PROP y ILL_CLO
DH['UG'][(DH['UG'] == 1) & ((DH['Alte'] == 'PROP') | (DH['Alte'] == 'ILL_CLO'))] = 12
## 3.- Finalmente, el resto pasa a ser definido como UG 11
DH['UG'][(DH['UG'] == 1)] = 11
## Mostramos el resultado final
DH
[21]:
| Este | Norte | Elevación | au_ppm | ag_ppm | cu_pct | aucn_ppm | cucn_ppm | Zmin | Alte | Lito | UG | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 472186.686 | 6925804.447 | 4220.763 | -99.00 | -99.0 | NaN | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 12 |
| 1 | 472187.202 | 6925805.493 | 4213.861 | 0.30 | 2.3 | 0.011 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 12 |
| 2 | 472187.343 | 6925805.770 | 4211.986 | 0.47 | 16.2 | 0.032 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 12 |
| 3 | 472187.493 | 6925806.060 | 4210.013 | 0.31 | 2.3 | 0.018 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 12 |
| 4 | 472187.642 | 6925806.351 | 4208.040 | 0.29 | 2.1 | 0.010 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 12 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 77836 | 471293.198 | 6925823.101 | 3761.674 | 0.03 | 1.0 | 0.002 | -99.0 | -99.0 | BACK | PROP | ECS | 21 |
| 77837 | 471293.540 | 6925824.040 | 3759.942 | 0.01 | 0.7 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 21 |
| 77838 | 471293.882 | 6925824.980 | 3758.209 | 0.01 | 0.4 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 21 |
| 77839 | 471294.224 | 6925825.920 | 3756.477 | 0.06 | 1.3 | 0.004 | -99.0 | -99.0 | BACK | PROP | ECS | 21 |
| 77840 | 471294.607 | 6925826.972 | 3754.538 | 0.06 | 0.5 | 0.003 | -99.0 | -99.0 | BACK | PROP | ECS | 21 |
77841 rows × 12 columns
[22]:
import numpy as np
import plotly.graph_objects as go
import scipy.stats as stats
from statsmodels.distributions.empirical_distribution import ECDF
# Plot all probability curves in the same figure
fig = go.Figure()
for category in sorted(DH.groupby('UG').groups.keys()):
values = DH.groupby('UG').get_group(category)['cu_pct'].dropna().values
# Sort values
values_sorted = np.sort(values)
n = len(values_sorted)
cumprob = (np.arange(1, n+1) - 0.5) / n
normal_scores = stats.norm.ppf(cumprob)
fig.add_trace(go.Scatter(
x=values_sorted,
y=normal_scores,
mode='markers',
marker=dict(size=5, opacity=0.7),
name=str(category)
))
# Custom y-axis to match imagen.png
y_ticks = stats.norm.ppf([0.0001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 0.99999])
y_ticklabels = ['0.001%','1%', '5%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '95%', '99%', '99.999%']
fig.update_layout(
title="Distribuciones preliminares de Dominios",
title_x=0.5,
xaxis_title='Cu [%]',
xaxis=dict(
type='log',
),
yaxis_title='Probabilidad',
yaxis=dict(
tickmode='array',
tickvals=y_ticks,
ticktext=y_ticklabels,
range=[stats.norm.ppf(0.00001), stats.norm.ppf(0.99999)]
),
width=800,
height=600,
font_family="Times New Roman",
font_size=14,
font_color="black",
plot_bgcolor='white',
autosize=False,
showlegend=True
)
fig.update_layout(
legend_title_text='UGs',
legend=dict(
itemsizing='constant',
font=dict(size=14),
bgcolor='rgba(255,255,255,0.8)',
bordercolor='black',
borderwidth=1
)
)
fig.update_xaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_yaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_layout(hovermode=False)
fig.show()
En cualquier punto, es importante generar las estadisticas basicas de los resultados preliminares. Esto nos ayuda a tomar decisiones sobre que UGs juntar y cuales no, de acuerdo a las medias, varianzas y cantidad de muestras por Unidad
[23]:
stats_by_mine = DH.groupby(['UG']).describe().round(2)
stats_by_mine['cu_pct']
[23]:
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| UG | ||||||||
| 0 | 3030.0 | 0.01 | 0.02 | 0.0 | 0.00 | 0.00 | 0.00 | 0.27 |
| 11 | 2187.0 | 0.08 | 0.14 | 0.0 | 0.02 | 0.03 | 0.05 | 1.82 |
| 12 | 5325.0 | 0.04 | 0.06 | 0.0 | 0.01 | 0.02 | 0.04 | 0.73 |
| 13 | 9754.0 | 0.07 | 0.10 | 0.0 | 0.02 | 0.03 | 0.05 | 1.50 |
| 21 | 14857.0 | 0.06 | 0.14 | 0.0 | 0.00 | 0.02 | 0.06 | 8.00 |
| 22 | 8812.0 | 0.14 | 0.16 | 0.0 | 0.04 | 0.09 | 0.19 | 2.75 |
| 23 | 32942.0 | 0.24 | 0.24 | 0.0 | 0.11 | 0.20 | 0.32 | 8.30 |
De manera de tener unidades balanceadas de un punto de vista del número de muestras y considerando a su vez las medias de las unidades preliminares, finalmente tomamos la siguiente decisión:
Reagrupación de unidades preliminares en Unidades Geológicas Finales:
UG de menor ley: Asignar UG 0 a todas las muestras con UG igual a 1.
UG con segunda menor ley: Asignar UG 1 a todas las muestras con UG igual a 12, 11, 13 o 21.
UG con segunda mayor ley: Asignar UG 2 a todas las muestras con UG igual a 22.
UG con mayor ley: Asignar UG 3 a todas las muestras con UG igual a 23.
[24]:
## 1.- UG de menor ley
DH['UG'][(DH['UG'] == 0)] = 0
## 2.- UG con segunda menor ley
DH['UG'][(DH['UG'] == 12) | (DH['UG'] == 11) | (DH['UG'] == 13) | (DH['UG'] == 21)] = 1
## 3.- UG con segunda mayor ley
DH['UG'][(DH['UG'] == 22)] = 2
## 4.- UG con mayor ley
DH['UG'][(DH['UG'] == 23)] = 3
DH
[24]:
| Este | Norte | Elevación | au_ppm | ag_ppm | cu_pct | aucn_ppm | cucn_ppm | Zmin | Alte | Lito | UG | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 472186.686 | 6925804.447 | 4220.763 | -99.00 | -99.0 | NaN | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 1 | 472187.202 | 6925805.493 | 4213.861 | 0.30 | 2.3 | 0.011 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 2 | 472187.343 | 6925805.770 | 4211.986 | 0.47 | 16.2 | 0.032 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 3 | 472187.493 | 6925806.060 | 4210.013 | 0.31 | 2.3 | 0.018 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| 4 | 472187.642 | 6925806.351 | 4208.040 | 0.29 | 2.1 | 0.010 | -99.0 | -99.0 | OXI | ILL_CLO | IBX_MM | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 77836 | 471293.198 | 6925823.101 | 3761.674 | 0.03 | 1.0 | 0.002 | -99.0 | -99.0 | BACK | PROP | ECS | 1 |
| 77837 | 471293.540 | 6925824.040 | 3759.942 | 0.01 | 0.7 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 1 |
| 77838 | 471293.882 | 6925824.980 | 3758.209 | 0.01 | 0.4 | 0.001 | -99.0 | -99.0 | BACK | PROP | ECS | 1 |
| 77839 | 471294.224 | 6925825.920 | 3756.477 | 0.06 | 1.3 | 0.004 | -99.0 | -99.0 | BACK | PROP | ECS | 1 |
| 77840 | 471294.607 | 6925826.972 | 3754.538 | 0.06 | 0.5 | 0.003 | -99.0 | -99.0 | BACK | PROP | ECS | 1 |
77841 rows × 12 columns
[25]:
import numpy as np
import plotly.graph_objects as go
import scipy.stats as stats
from statsmodels.distributions.empirical_distribution import ECDF
# Plot all probability curves in the same figure
fig = go.Figure()
for category in sorted(DH.groupby('UG').groups.keys()):
values = DH.groupby('UG').get_group(category)['cu_pct'].dropna().values
# Sort values
values_sorted = np.sort(values)
n = len(values_sorted)
cumprob = (np.arange(1, n+1) - 0.5) / n
normal_scores = stats.norm.ppf(cumprob)
fig.add_trace(go.Scatter(
x=values_sorted,
y=normal_scores,
mode='markers',
marker=dict(size=5, opacity=0.7),
name=str(category)
))
# Custom y-axis to match imagen.png
y_ticks = stats.norm.ppf([0.0001, 0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 0.99999])
y_ticklabels = ['0.001%','1%', '5%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '95%', '99%', '99.999%']
fig.update_layout(
title="Distribución Estadística de Unidades Geológicas",
title_x=0.5,
xaxis_title='Cu [%]',
xaxis=dict(
type='log',
),
yaxis_title='Probabilidad',
yaxis=dict(
tickmode='array',
tickvals=y_ticks,
ticktext=y_ticklabels,
range=[stats.norm.ppf(0.00001), stats.norm.ppf(0.99999)]
),
width=800,
height=600,
font_family="Times New Roman",
font_size=14,
font_color="black",
plot_bgcolor='white',
autosize=False,
showlegend=True
)
fig.update_layout(
legend_title_text='UGs',
legend=dict(
itemsizing='constant',
font=dict(size=14),
bgcolor='rgba(255,255,255,0.8)',
bordercolor='black',
borderwidth=1
)
)
fig.update_xaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_yaxes(gridcolor='rgba(0,0,0,0.1)')
fig.update_layout(hovermode=False)
fig.show()
[26]:
stats_by_mine = DH.groupby(['UG']).describe().round(2)
stats_by_mine['cu_pct']
[26]:
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| UG | ||||||||
| 0 | 3030.0 | 0.01 | 0.02 | 0.0 | 0.00 | 0.00 | 0.00 | 0.27 |
| 1 | 32123.0 | 0.06 | 0.12 | 0.0 | 0.01 | 0.02 | 0.06 | 8.00 |
| 2 | 8812.0 | 0.14 | 0.16 | 0.0 | 0.04 | 0.09 | 0.19 | 2.75 |
| 3 | 32942.0 | 0.24 | 0.24 | 0.0 | 0.11 | 0.20 | 0.32 | 8.30 |
Procedemos a desplegar espacialmente nuestras Unidades Geológicas (UG) en el espacio 3D, de manera de verificar que ellas hagan sentido desde un punto de vista espacial (que se encuentren relativamente contiguas las muestras pertenecientes a una misma UG y que no se encuentren truncadas las UGs entre ellas de forma tan evidente).
[27]:
DH["UG"] = DH["UG"].astype(str)
fig = px.scatter_3d(DH, x='Este', y='Norte', z='Elevación', color='UG',category_orders={'UG': sorted(DH.groupby('UG').groups.keys())})
fig.update_traces(marker=dict(size=2.0))
# Mejor estilo para leyenda
fig.update_layout(
title_text='Unidades Geológicas para Cu',
legend_title_text='UGs',
legend=dict(
itemsizing='constant',
font=dict(size=14),
bgcolor='rgba(255,255,255,0.8)',
bordercolor='black',
borderwidth=1
), hovermode=False
)
fig.show()
Omitimos para este caso el uso de la variable Litología, de modo de no complejizar aún más esta demostración.
6.2. Exportar resultados
Finalmente, exportamos nuestros resultados para no tener que volver a escribir generar estas variables de UGs en los próximos flujos de trabajo.
[29]:
DH.to_csv('Data_sin_compositar_con_UGs.csv',index=False, encoding='latin1')